﻿#define UNROLL

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Warburton.FastBitArray.Factories;

namespace Warburton.FastBitArray.Implementations
{
    class Safe64BitBitArray
        : IBitArray
    {
        private Factories.Safe64BitFactory _factory;
        private int _length;
        private ulong[] _bits;

        public Safe64BitBitArray(Safe64BitFactory factory, int length)
        {
            _factory = factory;
            _length = length;
            if (_length > 0)
                _bits = new ulong[((length - 1) / 64) + 1];
        }

        public IBitArray Set(int index, bool value)
        {
            if (_length == 0 || index >= _length)
                throw new IndexOutOfRangeException();

            var pos = index / 64;
            var bit = index % 64;

            var bitPos = 1UL << bit;
            if (value)
                _bits[pos] |= bitPos;
            else
                _bits[pos] &= ~bitPos;

            return this;
        }

        public IBitArray SetAll(bool value)
        {
            if (_length > 0)
            {
                var v = value ? 0xFFFFFFFFFFFFFFFF : 0;
                var length = _bits.Length;
                for (int i = 0; i < length; ++i)
                    _bits[i] = v;
            }
            return this;
        }

        public IBitArray And(IBitArray value)
        {
            var rhs = value as Safe64BitBitArray;
            if (rhs == null)
                throw new ArgumentException();
            if (rhs._length != _length)
                throw new ArgumentException();

            var count = _bits.Length;
            int i = 0;
#if UNROLL
            for (; i < count - 4; i += 4)
            {
                _bits[i + 0] &= rhs._bits[i + 0];
                _bits[i + 1] &= rhs._bits[i + 1];
                _bits[i + 2] &= rhs._bits[i + 2];
                _bits[i + 3] &= rhs._bits[i + 3];
            }
#endif
            for (; i < count; ++i)
                _bits[i] &= rhs._bits[i];

            return this;
        }

        public IBitArray Or(IBitArray value)
        {
            var rhs = value as Safe64BitBitArray;
            if (rhs == null)
                throw new ArgumentException();
            if (rhs._length != _length)
                throw new ArgumentException();

            var count = _bits.Length;
            int i = 0;
#if UNROLL
            for (; i < count - 4; i += 4)
            {
                _bits[i + 0] |= rhs._bits[i + 0];
                _bits[i + 1] |= rhs._bits[i + 1];
                _bits[i + 2] |= rhs._bits[i + 2];
                _bits[i + 3] |= rhs._bits[i + 3];
            }
#endif
            for (; i < count; ++i)
                _bits[i] |= rhs._bits[i];

            return this;
        }

        public IBitArray Xor(IBitArray value)
        {
            var rhs = value as Safe64BitBitArray;
            if (rhs == null)
                throw new ArgumentException();
            if (rhs._length != _length)
                throw new ArgumentException();

            var count = _bits.Length;
            int i = 0;
#if UNROLL
            for (; i < count - 4; i += 4)
            {
                _bits[i + 0] ^= rhs._bits[i + 0];
                _bits[i + 1] ^= rhs._bits[i + 1];
                _bits[i + 2] ^= rhs._bits[i + 2];
                _bits[i + 3] ^= rhs._bits[i + 3];
            }
#endif
            for (; i < count; ++i)
                _bits[i] ^= rhs._bits[i];

            return this;
        }

        public IBitArray Not()
        {
            if (_length > 0)
            {
                var count = _bits.Length;
                var i = 0;
#if UNROLL
                for (; i < count - 4; i += 4)
                {
                    _bits[i + 0] = ~_bits[i + 0];
                    _bits[i + 1] = ~_bits[i + 1];
                    _bits[i + 2] = ~_bits[i + 2];
                    _bits[i + 3] = ~_bits[i + 3];
                }
#endif
                for (; i < count; ++i)
                    _bits[i] = ~_bits[i];
            }
            return this;
        }

        public IBitArrayFactory Factory
        {
            get { throw new NotImplementedException(); }
        }

        public int NumberOfBits
        {
            get { return _length; }
        }

        public bool Get(int index)
        {
            if (_length == 0 || index >= _length)
                throw new IndexOutOfRangeException();

            var pos = index / 64;
            var bit = index % 64;

            var bitPos = 1UL << bit;
            return (_bits[pos] & bitPos) == bitPos;
        }

        public IEnumerable<int> GetIndexes(bool value)
        {
            var bitPos = 0UL;
            var index = 0;
            var current = 0UL;

            for (int i = 0; i < _length; ++i)
            {
                if (bitPos > 0)
                    bitPos <<= 1;
                else
                {
                    current = _bits[index];
                    bitPos = 1;
                    ++index;
                }
                var bit = (current & bitPos);
                if ((bit == 0 && !value) || (bit != 0 && value))
                    yield return i;
            }
        }
    }
}
